package com.delivery.controller;
import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.delivery.config.WxPayConfig;
import com.delivery.constant.DeliveryConstant;
import com.delivery.constant.DeliveryConstantEnum;
import com.delivery.entity.Order;
import com.delivery.entity.OrderCompensation;
import com.delivery.mapper.OrderCompensationMapper;
import com.delivery.mapper.OrderMapper;
import com.delivery.utils.WechatPay2ValidatorForRequest;
import com.delivery.utils.WxMiniPayUtils;
import com.delivery.utils.WxdecryptPayinformUtils;
import com.delivery.vo.DownOrderResultVo;
import com.google.gson.Gson;
import com.kuaidi100.sdk.api.BOrderOfficial;
import com.kuaidi100.sdk.contant.ApiInfoConstant;
import com.kuaidi100.sdk.core.IBaseClient;
import com.kuaidi100.sdk.pojo.HttpResult;
import com.kuaidi100.sdk.request.BOrderReq;
import com.kuaidi100.sdk.request.PrintReq;
import com.kuaidi100.sdk.utils.SignUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@RestController
@RequestMapping("/pay")
public class payCallbackController {

    @Autowired
    private WxdecryptPayinformUtils wxdecryptPayinformUtils;
    @Autowired
    OrderMapper orderMapper;
    @Autowired
    WxPayConfig autoWxPayConfig;

    @Resource(name = "taskExecutor")
    Executor executor;

    @Autowired
    private DeliveryConstant deliveryConstant;
    @Autowired
    private OrderCompensationMapper orderCompensationMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private String prefix = "delivery-order:";

    private String prefixCompensation = "delivery-order-compensation:";

    @Value("${constant.callBackUrl}")
    private String callBackUrl;

  @PostMapping("/callback")
  public String success(HttpServletRequest request, HttpServletResponse response) {
        Gson gson = new Gson();
        Map<String, String> map = new HashMap<>();
        try {
            String body = readData(request);
            Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
            log.error(body);
            String requestId = (String) bodyMap.get("id");
//签名的验证
            //初始化verifier
            WxMiniPayUtils.initWXPayClient();
            WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
                    = new WechatPay2ValidatorForRequest(WxMiniPayUtils.verifier, requestId, body);
            if(!wechatPay2ValidatorForRequest.validate(request)){
                log.error("通知验签失败");
                //失败应答
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "通知验签失败");
                return gson.toJson(map);
            }

            //解密报文
            String plainText = wxdecryptPayinformUtils.decryptFromResource(bodyMap);
            //将明文转换成map
            Gson gsonDecrypt = new Gson();
            HashMap plainTextMap = gsonDecrypt.fromJson(plainText, HashMap.class);
            log.info("通知验签成功");

            switch (Integer.valueOf((String) plainTextMap.get("attach")) ) {
                case 1:
                    // 处理寄件支付
                    deliveryPay(plainTextMap);
                    break;
                case 2:
                    // 处理补缴支付
                    compensationPay(plainTextMap);

                    break;
                default:
                    log.info("未知的支付类型");
                    break;
            }



            //成功应答
            response.setStatus(200);
            //返回应答报文
            map.put("code", "SUCCESS");
            map.put("message", "成功");
            return gson.toJson(map);

        } catch (Exception e) {
            e.printStackTrace();
            //失败应答
            response.setStatus(500);
            //返回状态码
            map.put("code", "ERROR");
            //返回应答报文
            map.put("message", "失败");
            return gson.toJson(map);
        }

    }



    @RequestMapping("/refundWeChatNotify")
  public String refundWeChatNotify(HttpServletRequest request, HttpServletResponse response)  {
            Gson gson = new Gson();
            Map<String, String> map = new HashMap<>();
            try {
                String body = readData(request);
                Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
                log.error(body);
                String requestId = (String) bodyMap.get("id");
//签名的验证
                //初始化verifier
                WxMiniPayUtils.initWXPayClient();
                WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
                        = new WechatPay2ValidatorForRequest(WxMiniPayUtils.verifier, requestId, body);
                
                if(!wechatPay2ValidatorForRequest.validate(request)){
                    log.error("通知验签失败");
                    //失败应答
                    response.setStatus(500);
                    map.put("code", "ERROR");
                    map.put("message", "通知验签失败");
                    return gson.toJson(map);
                }

                //解密报文
                String plainText = wxdecryptPayinformUtils.decryptFromResource(bodyMap);
                //将明文转换成map
                Gson gsonDecrypt = new Gson();
                HashMap plainTextMap = gsonDecrypt.fromJson(plainText, HashMap.class);
                log.info("通知验签成功");

                Order order = orderMapper.selectOne(
                        new LambdaQueryWrapper<Order>()
                                .eq(Order::getCode, plainTextMap.get("out_trade_no").toString())
                );
                //更新状态
                if (order != null && order.getPayStatus() == DeliveryConstantEnum.StatusEnum.alreadyPay.getStatus()){
                    order.setPayStatus(DeliveryConstantEnum.StatusEnum.refunded.getStatus());
                    order.setOutRefundNo(plainTextMap.get("out_refund_no").toString());
                    orderMapper.updateById(order);
                }

            //返回给微信
            response.setStatus(200);
            //返回应答报文
            map.put("code", "SUCCESS");
            map.put("message", "成功");
            return gson.toJson(map);
            
        }catch (Exception e){
           e.printStackTrace();
          //失败应答
           response.setStatus(500);
           //返回状态码
          map.put("code", "ERROR");
          //返回应答报文
          map.put("message", "失败");
          log.error("v3处理微信退款异步回调：错误-"+e.getMessage());
          return gson.toJson(map);
        }

    }

  private String  readData(HttpServletRequest request) throws IOException {
        ServletInputStream is = request.getInputStream();
        byte[] bs = new byte[1024];
        int len = -1;
        StringBuilder builder = new StringBuilder();
        while ((len = is.read(bs)) != -1){
            builder.append(new String(bs,0,len));
        }

        return builder.toString();
    }
  private void pushkuaidi100(Order order) throws Exception {

        PrintReq printReq = new PrintReq();
        BOrderReq bOrderReq = new BOrderReq();
        bOrderReq.setKuaidicom(order.getKuaidiCoding());

        bOrderReq.setSendManName(order.getSendManName());
        bOrderReq.setSendManMobile(order.getSendManMobile());
        bOrderReq.setSendManPrintAddr(order.getSendManPrintAddr());

        bOrderReq.setRecManName(order.getRecManName());
        bOrderReq.setRecManMobile(order.getRecManMobile());
        bOrderReq.setRecManPrintAddr(order.getRecManPrintAddr());

        bOrderReq.setCallBackUrl(callBackUrl);
        bOrderReq.setWeight(order.getWeight());
        bOrderReq.setCargo(order.getCargo());
        bOrderReq.setValinsPay(order.getValinsPay());
        bOrderReq.setRemark(order.getRemark());

        String t = String.valueOf(System.currentTimeMillis());
        String param = new Gson().toJson(bOrderReq);

      String md5 = SignUtils.printSign(param, t, deliveryConstant.key, deliveryConstant.secret);
      printReq.setKey(deliveryConstant.key);
        printReq.setSign(md5);
        printReq.setT(t);
        printReq.setParam(param);
        printReq.setMethod(ApiInfoConstant.B_ORDER_OFFICIAL_ORDER_METHOD);
        IBaseClient bOrder = new BOrderOfficial();
        HttpResult execute = bOrder.execute(printReq);
        String body = execute.getBody();

        DownOrderResultVo downOrderResultVo = JSON.parseObject(body, DownOrderResultVo.class);

        if (downOrderResultVo.getResult()){
            //设置初始状态
            order.setOrderStatus(0);
            //设置任务id
            order.setTaskid(downOrderResultVo.getData().getTaskId());
            //设置快递单号
            order.setKuaidinum(downOrderResultVo.getData().getKuaidinum());
            //设置快递100 的订单号
            order.setKuaidi100Id(downOrderResultVo.getData().getOrderId());

            //更新数据库
            orderMapper.insert(order);
        }else {

            // 异步退钱
            CompletableFuture.runAsync(() -> {
                JSONObject domesticRefundsJSON = new JSONObject();
                domesticRefundsJSON.put("transaction_id", order.getTransactionId());
                //雪花算法生成退款订单号
                String outRefundNo = new Snowflake().nextIdStr();
                domesticRefundsJSON.put("out_refund_no", outRefundNo);
                domesticRefundsJSON.put("reason","下单失败:寄件详细地址信息表述不清楚,将无法派送,请调整地址");
                JSONObject amount = new JSONObject();
                amount.put("refund", (order.getOrderAmount().multiply(new BigDecimal("100"))).intValue()  );
                amount.put("total",  (order.getOrderAmount().multiply(new BigDecimal("100"))).intValue());
                amount.put("currency","CNY");
                domesticRefundsJSON.put("amount",amount);
                domesticRefundsJSON.put("notify_url", autoWxPayConfig.getRefundsNotifyDomain() );
                String parm = domesticRefundsJSON.toJSONString();
                String domesticRefundsBody = null;
                try {
                    domesticRefundsBody = WxMiniPayUtils.v3Post("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds",parm);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }, executor);


        }


    }
  private void deliveryPay(HashMap plainTextMap) {
        //获取订单号
        String out_trade_no = (String) plainTextMap.get("out_trade_no");

        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<Order>().eq(Order::getCode, out_trade_no);
        Order order = orderMapper.selectOne(wrapper);

        if (order == null){
            String redisStr = stringRedisTemplate.opsForValue().get(prefix + out_trade_no);
            //JSON字符串转换成Java对象
            Order redisOrder = JSONObject.parseObject(redisStr, Order.class);
            redisOrder.setPayStatus(DeliveryConstantEnum.StatusEnum.alreadyPay.getStatus());
            redisOrder.setTransactionId(plainTextMap.get("transaction_id").toString());

            //推送订单到快递公司
            CompletableFuture.runAsync(() -> {
                try {
                    pushkuaidi100(redisOrder);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }, executor);

            //删除redis缓存
            CompletableFuture.runAsync(()->{
                stringRedisTemplate.opsForValue().decrement(prefix + out_trade_no);
            },executor);
        }
    }

  private void compensationPay(HashMap plainTextMap) {
        //获取商户订单号
        String out_trade_no = (String) plainTextMap.get("out_trade_no");
        String redisStr = stringRedisTemplate.opsForValue().get(prefixCompensation + out_trade_no);
        //JSON字符串转换成Java对象
        OrderCompensation orderCompensation = JSONObject.parseObject(redisStr, OrderCompensation.class);
        if (orderCompensation!=null && orderCompensation.getPayStatus() == DeliveryConstantEnum.StatusEnum.unpaid.getStatus()){
                orderCompensation.setCode(out_trade_no);
                orderCompensation.setPayStatus(DeliveryConstantEnum.StatusEnum.alreadyPay.getStatus());
                orderCompensationMapper.updateById(orderCompensation);
        }
    }


}
